home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Diamond Collection
/
The Diamond Collection (Software Vault)(Digital Impact).ISO
/
cdr40
/
x1j4_src.zip
/
IPROUTE.C
< prev
next >
Wrap
Text File
|
1995-01-17
|
42KB
|
1,493 lines
/*****************************************************************************/
/* */
/* */
/* ***** ***** */
/* ***** ***** */
/* ***** ***** */
/* ***** ***** */
/* *************** *************** */
/* ***************** ***************** */
/* *************** *************** */
/* ***** ***** TheNet */
/* ***** ***** Portable. Compatible. */
/* ***** ***** Public Domain */
/* ***** ***** G8KBB */
/* */
/* This software is public domain ONLY for non commercial use */
/* */
/* */
/*****************************************************************************/
/* Level 3, Internet Gateway */
/* Version 1.00 */
/* Dave Roberts G8KBB, 7, Rowanhayes Close, Ipswich, England */
/* 10-April-91 */
/* This software is released into the public domain on the understanding
* that it is to be used only for non life threatening, amateur, non
* commercial purposes only.
* It has been written expressly for use in self tuition of
* people in amateur radio communications only. It is NOT claimed that this
* software works correctly.
*
* USELONG, if defined, causes long integers to be used where appropriate
* so failing to define it will avoid longs completely !
*
* September 1993 - released as TheNet X-1J
*/
#include "all.h"
#include "tntyp.h" /* Definition of structures */
#include "ip.h"
#include "icmp.h"
#ifdef BANKED
#define EXTERN extern
#else
#define EXTERN
#endif
#include "ipv.h"
/*---------------------------------------------------------------------------*/
/* Strings etc */
/* These are the interface handlers.
* Number Interface
* 0 Netrom
* 1 port 0 AX.25
* 2 port 1 AX.25
*
* The order MUST match the names in if_names[] in tnl7ip.c
*/
I_FACE interfaces[] =
{
#ifdef MOD_MTU
nr_iface, &mtu_ipn, ARP_NETROM, 0,
l2_iface, &mtu_ip0, ARP_AX25, HDLCPORT,
l2_iface, &mtu_ip1, ARP_AX25, ASYNPORT,
#else
nr_iface, 256-20, ARP_NETROM, 0,
l2_iface, 256, ARP_AX25, HDLCPORT,
l2_iface, 256, ARP_AX25, ASYNPORT,
#endif
};
char nodigi[] = { '\000' };
char QST[] = { 'Q','S','T',' ',' ',' ','\140' };
/* ***************************************************************************
* Function : ipinit
*
* Inputs : none
*
* Returns : none
*
* Operation : Initialisation of IP gateway on reset of the TNC
*
* -------------------------------------------------------------------------*/
VOID ipinit() /* Router initialisation */
{
register int i;
inithd(&iprxfl);
inithd(&arprxfl);
if (!iswarm()) /* Warmstart then skip */
{
inithd(&IP_Routes);
inithd(&Arp_tab);
#ifdef USELONG
my_ip_addr.Long = DEFMYIPADDR.Long; /* node IP address */
bcast_ip_addr.Long = DEFIPBCAST.Long; /* broadcast addr */
#else
my_ip_addr.Short[1] = DEFMYIPADDR.Short[1]; /* node IP */
my_ip_addr.Short[0] = DEFMYIPADDR.Short[0]; /* ..address*/
bcast_ip_addr.Short[1] = DEFIPBCAST.Short[1]; /* broadcast*/
bcast_ip_addr.Short[0] = DEFIPBCAST.Short[0]; /* ..address*/
#endif
for( i=0; i<=NUMIPMIB; i++ )
Ip_mib[i].value.integer = 0; /* clear IP stats */
ipL2Modes = DEFIPL2MODES; /* l2 modes */
ARPrunning = ipForwarding = DEFIPENABLE;/* enable ip router */
ipDefaultTTL = DEFIPTTL; /* set default ttl */
ipReasmTimeout = TLB; /* reasm timeout */
ARPcounter = ARPtimer = 60;
}
else /* Warmstart */
{
}
IP_Route_Cache.route = NULLROUTE; /* clear route cache */
}
/* ***************************************************************************
* Function : IP server programme. Called by main scheduler
*
* Inputs : None
*
* Returns : None
*
* Operation : Reads all frames from ip receive queue and handles them.
* Then calls ARP protocol handler with its frames
* -------------------------------------------------------------------------*/
VOID ipserv()
{
register mhtyp *mbhd;
/* Firstly, run through the queued IP frames
*/
while ((mbhd = (mhtyp *)iprxfl.lnext) != (mhtyp *) &iprxfl)
{
unlink( mbhd );
#ifdef PK96
sta_led_on();
#endif
ip_route( mbhd ); /* pass to IP router routine */
#ifdef PK96
sta_led_off();
#endif
dealmb( mbhd );
}
/* Now run through the queued ARP frames
*/
while ((mbhd = (mhtyp *)arprxfl.lnext) != (mhtyp *) &arprxfl)
{
unlink( mbhd );
arp_service( mbhd ); /* pass to IP router routine */
dealmb( mbhd );
}
}
/* ***************************************************************************
* Function : IP router for a single frame. Called by ip server
*
* Inputs : Pointer to frame to be routed
*
* Returns : None
*
* Operation : Forwards frame or returns an error ( ICMP ) frame.
* Fragments the frame if sub layer demands it
* -------------------------------------------------------------------------*/
VOID ip_route( frame ) /* Internet server */
mhtyp *frame;
{
register int i; /* a counter of general nature */
register mhtyp *mbhd = frame; /* pointer to frame using a register*/
register mhtyp *tbp; /* pointer to frame for outputting */
char *rxnxt; /* temp for saving mbhd position */
#ifndef BANKED
static IP ip; /* structure for decoded ip header */
#endif
int strict; /* option sets for strict routing */
ipaddr gateway; /* addr of next IP router for frame */
IP_ROUTE *rp; /* pointer used in finding route */
unsigned length; /* length of data portion of frame */
unsigned offset; /* offset in fragmentation routine */
BOOLEAN rxbroadcast; /* set denotes broadcast frame */
BOOLEAN mf_flag; /* more flag in fragmentation */
unsigned char *opt, *ptr; /* pointers in option handling */
unsigned char opt_len; /* length in option handling */
unsigned ip_len; /* ip header length */
unsigned mtu; /* max port packet size for output */
if(((unsigned char)mbhd->pid != PID_IP) || !ipForwarding )
return;
rxnxt = mbhd->nxtchr; /* Save pointers to IP hdr start */
ip_len = getchr( mbhd ); /* get length & version byte*/
ip.version = ( ip_len >> 4 ) & 0x0f; /* extract version number */
ip_len = ( ip_len & 0x0f ) << 2; /* and correct length */
mbhd->nxtchr = rxnxt; /* reinstate the pointer */
if( ( mbhd->putcnt - (--mbhd->getcnt) ) < IPLEN
||
ip_len < IPLEN
||
ip.version != IPVERSION
||
cksum( NULLHEADER, mbhd, ip_len ) != 0 )
{
ipInHdrErrors++;
return;
}
/* Now extract frame IP header
* This is akin to ntohip() in NOS
*/
getchr( mbhd ); /* skip length/version */
ip.tos = getchr( mbhd );
ip.length = get16( mbhd );
ip.id = get16( mbhd );
ip.offset = get16( mbhd );
ip.flags.mf = ( ip.offset & 0x2000 ) ? 1 : 0;
ip.flags.df = ( ip.offset & 0x4000 ) ? 1 : 0;
ip.offset = ( ip.offset & 0x1fff ) << 3;
ip.ttl = getchr( mbhd );
ip.protocol = getchr( mbhd );
ip.checksum = get16( mbhd );
ip.source.Short[1] = get16( mbhd );
ip.source.Short[0] = get16( mbhd );
ip.dest.Short[1] = get16( mbhd );
ip.dest.Short[0] = get16( mbhd );
/* If this is a broadcast packet, set flag
* Note we do not do port addressing here !
*/
rxbroadcast = is_broadcast_address( &ip.dest );
/* extract optional options from frame
*/
if( (ip.optlen = ip_len - IPLEN ) != 0 )
for( i=0; i < ip.optlen; i++ )
ip.options[i] = getchr( mbhd );
length = ip.length - ip_len;
/* trim_mbuf( don't bother with this in this version ! );
*/
if( !rxbroadcast && nmbfre < 256 )
icmp_output( &ip, mbhd, ICMP_QUENCH, 0, NULLICMP );
/* handle options here
*/
strict = 0;
for( opt = ip.options; opt < &ip.options[ip.optlen]; opt += opt_len )
{
opt_len = opt[1];
switch( opt[0] & OPT_NUMBER )
{
case IP_EOL:
goto no_opt;
case IP_NOOP:
opt_len = 1;
break;
case IP_SSROUTE:
strict = 1;
case IP_LSROUTE:
if( !is_my_ip_addr( &ip.dest ) )
break;
if( opt[2] >= opt_len )
break;
ptr = opt+opt[2]-1;
for( i=0; i<4; i++ )
{
ip.dest.Bytes[3-i] = ptr[i];
ptr[i] = my_ip_addr.Bytes[3-i];
}
opt[2] += 4;
break;
case IP_RROUTE:
if( opt[2] >= opt_len )
{
if( !rxbroadcast )
{
union icmp_args icmp_args;
icmp_args.pointer = IPLEN+opt-ip.options;
icmp_output(&ip,mbhd,ICMP_PARAM_PROB,0,&icmp_args);
}
return;
}
ptr = opt+opt[2]-1;
for( i=0; i<4; i++ )
ptr[i] = my_ip_addr.Bytes[3-i];
opt += 4;
break;
}
}
no_opt:
if( ( is_my_ip_addr( &ip.dest ) ) || rxbroadcast )
{
if( !rxbroadcast && !ip.flags.mf && ip.offset == 0 &&
ip.protocol == ICMP_PTCL )
{
icmp_input( &ip, mbhd );
ipInReceives++;
return;
}
else
{
if( !rxbroadcast )
icmp_output( &ip, mbhd, ICMP_DEST_UNREACH,
ICMP_PROT_UNREACH, NULLICMP );
ipInUnknownProtos++;
return;
}
}
/* if( i_iface != NULLIF ) etc etc */
ipForwDatagrams++;
/* timeout - kill & return error frame to sender
*/
if( --ip.ttl == 0 )
{
icmp_output( &ip, mbhd, ICMP_TIME_EXCEED, 0, NULLICMP );
ipInHdrErrors++;
return;
}
/* find entry in routing table for this frame.
* If there isn't one - then return an error & trash frame
*/
if( ( rp = rt_find( &ip.dest ) ) == NULLROUTE )
{
icmp_output( &ip, mbhd, ICMP_DEST_UNREACH,
ICMP_HOST_UNREACH, NULLICMP );
ipOutNoRoutes++;
return;
}
/* rp->uses++ */
/* redirection goes here if wanted */
/* Compute the next IP router to send to ( ie the gateway )
* If the route table entry has no gateway, use destination
* If the gateway is not the destination, and strict routing
* has been called for, then error !
*/
#ifdef USELONG
gateway.Long = rp->gateway.Long == 0L ? ip.dest.Long : rp->gateway.Long;
if( strict && gateway.Long != ip.dest.Long )
#else
i = (rp->gateway.Short[0] == 0) && ( rp->gateway.Short[1] == 0 );
gateway.Short[0] = i ? ip.dest.Short[0] : rp->gateway.Short[0];
gateway.Short[1] = i ? ip.dest.Short[1] : rp->gateway.Short[1];
if( strict && !( ( gateway.Short[0] == ip.dest.Short[0] ) &&
( gateway.Short[1] == ip.dest.Short[1] ) ) )
#endif
{
icmp_output( &ip, mbhd, ICMP_DEST_UNREACH,
ICMP_ROUTE_FAIL, NULLICMP );
ipOutNoRoutes++;
return;
}
/* bomb out if a funny has happened ( to preserve node integrity )
*/
if( rp->interface >= MAXINTERFACES )
return;
/* mtu for interface determines whether frame needs fragmentation
*/
#ifdef MOD_MTU
mtu = *(interfaces[rp->interface].mtu);
#else
mtu = interfaces[rp->interface].mtu;
#endif
if( ip.length <= mtu )
{
tbp = htonip( &ip, mbhd, 0 );
/* if( tbp == NULLBUF )
return;
*/ (*interfaces[rp->interface].func)( tbp, rp->interface,
&gateway, ip.tos );
return;
}
/* fragmentation needed for here on.
* First point -if it is marked don't fragment, we have a problem
*/
if( ip.flags.df )
{
union icmp_args icmp_args;
icmp_args.mtu = mtu;
icmp_output(&ip,mbhd,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,&icmp_args );
ipFragFails++;
return;
}
/* prepare for fragmentation & go for it !
*/
offset = ip.offset;
mf_flag = ip.flags.mf;
while( length != 0 )
{
unsigned fragsize;
ip.offset = offset;
if( length + ip_len <= mtu )
{
fragsize = length;
ip.flags.mf = mf_flag;
}
else
{
fragsize = (mtu - ip_len ) & 0xfff8;
ip.flags.mf = 1;
}
ip.length = fragsize + ip_len;
tbp = htonip( &ip, NULLBUF, 0 );
for( i=fragsize; i != 0; i-- )
putchr( getchr( mbhd ), tbp );
(*interfaces[rp->interface].func)( tbp, rp->interface,
&gateway, ip.tos );
ipFragCreates++;
offset += fragsize;
length -= fragsize;
}
ipFragOKs++;
return;
}
/* ***************************************************************************
* Function : Determine if this address is my address
*
* Inputs : pointer to address to be checked
*
* Returns : TRUE if addresses match and an address has been set
* FALSE if my address zero or no address match
*
* Operation :
* -------------------------------------------------------------------------*/
is_my_ip_addr( address )
register ipaddr *address;
{
#ifdef USELONG
return( my_ip_addr.Long != 0L && my_ip_addr.Long == address->Long );
#else
if( (my_ip_addr.Short[0] == 0) && (my_ip_addr.Short[1] == 0) )
return( 0 );
return( (my_ip_addr.Short[0] == address->Short[0]) &&
(my_ip_addr.Short[1] == address->Short[1]) );
#endif
}
/* ***************************************************************************
* Function : Check whether this address is the IP broadcast_ip address
*
* Inputs : Pointer to IP address
*
* Returns : TRUE if my address set and addresses match exactly
* FALSE otherwise
*
* Operation :
* -------------------------------------------------------------------------*/
is_broadcast_address( address )
register ipaddr *address;
{
#ifdef USELONG
return( bcast_ip_addr.Long != 0L && bcast_ip_addr.Long == address->Long );
#else
if( (bcast_ip_addr.Short[0] == 0) && (bcast_ip_addr.Short[1] == 0) )
return( 0 );
return( (bcast_ip_addr.Short[0] == address->Short[0]) &&
(bcast_ip_addr.Short[1] == address->Short[1]) );
#endif
}
/* ***************************************************************************
* Function : Try to find an entry in routing table for given target
*
* Inputs : Pointer to IP address of target
*
* Returns : Either a pointer to the table entry or a null pointer if none
*
* Operation : First check the cache & return it if it is the one
* Then search the table which is stored in descending order
* of the number of significant bits, masking our address as
* we go.
* -------------------------------------------------------------------------*/
IP_ROUTE *rt_find( target )
register ipaddr *target;
{
register IP_ROUTE_MB *iprp;
ipaddr temp;
register unsigned bits;
#ifdef USELONG
if( IP_Route_Cache.target.Long == target->Long &&
IP_Route_Cache.route != NULLROUTE )
return( IP_Route_Cache.route );
temp.Long = target->Long;
#else
if( (IP_Route_Cache.target.Short[0] == target->Short[0] ) &&
(IP_Route_Cache.target.Short[1] == target->Short[1] ) &&
IP_Route_Cache.route != NULLROUTE )
return( IP_Route_Cache.route );
temp.Short[0] = target->Short[0];
temp.Short[1] = target->Short[1];
#endif
bits = 32;
for( iprp = (IP_ROUTE_MB *)IP_Routes.lnext;
iprp != (IP_ROUTE_MB *)&IP_Routes.lnext;
iprp = (IP_ROUTE_MB *)iprp->link.lnext )
{
#ifdef USELONG
if( bits > iprp->route.bits )
temp.Long &= ~0L << ( 32 - ( bits = iprp->route.bits ) );
if( iprp->route.dest.Long == temp.Long )
{
IP_Route_Cache.target.Long = target->Long;
return ( (IP_Route_Cache.route = &iprp->route) );
}
#else
if( bits > iprp->route.bits )
ip_mask( &temp, ( bits = iprp->route.bits ) );
if( (iprp->route.dest.Short[0] == temp.Short[0]) &&
(iprp->route.dest.Short[1] == temp.Short[1]) )
{
IP_Route_Cache.target.Short[0] = target->Short[0];
IP_Route_Cache.target.Short[1] = target->Short[1];
return ( (IP_Route_Cache.route = &iprp->route) );
}
#endif
}
return( NULLROUTE );
}
/* ***************************************************************************
* Function : Convert host IP format header to network format
*
* Inputs : pointer to header, buffer for data bytes & ckecksum flag
*
* Returns : pointer to the new buffer in network format
*
* Operation : Creates a new buffer,
* Transfers the header into it,
* and unless the data buffer is a null pointer, copies data
* -------------------------------------------------------------------------*/
mhtyp *htonip( iphdr, data, cflag )
register IP *iphdr;
mhtyp *data;
BOOLEAN cflag;
{
unsigned hdr_len;
register unsigned i;
register mhtyp *bufpoi;
unsigned fl_offs;
unsigned checksum;
char *ptr, *cksum_ptr;
hdr_len = IPLEN + iphdr->optlen;
bufpoi = (mhtyp *)allocb();
putchr( ( IPVERSION << 4 ) | ( hdr_len >> 2 ), bufpoi );
putchr( iphdr->tos, bufpoi );
put16( iphdr->length, bufpoi );
put16( iphdr->id, bufpoi );
fl_offs = iphdr->offset >> 3;
if( iphdr->flags.df )
fl_offs |= 0x4000;
if( iphdr->flags.mf )
fl_offs |= 0x2000;
put16( fl_offs, bufpoi );
putchr( iphdr->ttl, bufpoi );
putchr( iphdr->protocol, bufpoi );
cksum_ptr = bufpoi->nxtchr;
put16( 0, bufpoi );
put16( iphdr->source.Short[1], bufpoi );
put16( iphdr->source.Short[0], bufpoi );
put16( iphdr->dest.Short[1], bufpoi );
put16( iphdr->dest.Short[0], bufpoi );
for( i=0; i< iphdr->optlen; i++ )
putchr( iphdr->options[i], bufpoi );
ptr = bufpoi->nxtchr;
i = bufpoi->putcnt;
rwndmb( bufpoi );
checksum = cflag ? iphdr->checksum : cksum(NULLHEADER,bufpoi,hdr_len);
bufpoi->nxtchr = cksum_ptr;
bufpoi->putcnt = 10;
put16( checksum, bufpoi);
bufpoi->nxtchr = ptr;
bufpoi->putcnt = i;
if( data != NULLBUF )
{
#ifdef MODIFIED
mhtyp_copy( data, bufpoi );
#else
i = data->putcnt - data->getcnt;
while( i-- )
putchr( getchr( data ) , bufpoi );
#endif
}
return( bufpoi );
}
/* ***************************************************************************
* Function : Send an IP datagram.
* Modelled after the example interface on p 32 of RFC 791
*
* Inputs : er, read the comments below !
*
* Returns : nothing of any significance in this implementation
*
* Operation : Create network format header, append data to it to
* make a frame that looks like any other frame received
* and drop it back into the ip receive queue for ip_route
* -------------------------------------------------------------------------*/
int ip_send(source,dest,protocol,tos,ttl,bp,length,id,df)
ipaddr *source; /* source address */
ipaddr *dest; /* Destination address */
unsigned protocol; /* Protocol */
unsigned tos; /* Type of service */
unsigned ttl; /* Time-to-live */
mhtyp *bp; /* Data portion of datagram */
unsigned short length; /* Optional length of data portion */
unsigned short id; /* Optional identification */
unsigned df; /* Don't-fragment flag */
{
register mhtyp *tbp;
IP ip; /* Pointer to IP header */
register IP *ipptr = &ip;
#ifndef BANKED
static unsigned short id_cntr; /* Datagram serial number */
#endif
ipOutRequests++;
/* if(*source == INADDR_ANY) AAAARRRRRGGGHHHHH !!!
source.Long = my_ip_addr.Long;
*/ if(length == 0 && bp != NULLBUF)
length = bp->putcnt - bp->getcnt;
/* Fill in IP header */
ipptr->tos = tos;
ipptr->length = IPLEN + length;
ipptr->id = ( id == 0 ) ? id_cntr++ : id;
ipptr->offset = 0;
ipptr->flags.mf = 0;
ipptr->flags.df = df;
ipptr->ttl = ( ttl == 0 ) ? ipDefaultTTL : ttl;
ipptr->protocol = protocol;
#ifdef USELONG
ipptr->source.Long = source->Long;
ipptr->dest.Long = dest->Long;
#else
ipptr->source.Short[0] = source->Short[0];
ipptr->source.Short[1] = source->Short[1];
ipptr->dest.Short[0] = dest->Short[0];
ipptr->dest.Short[1] = dest->Short[1];
#endif
ipptr->optlen = 0;
tbp = htonip(&ip,bp,0);
dealmb(bp);
/* if( tbp == NULLBUF)
return( -1 );
*/ tbp->pid = PID_IP;
rwndmb( tbp );
relink( tbp, iprxfl.lprev );
return(0);
}
/* ***************************************************************************
* Function : ip router interface handler for netrom interface
*
* Inputs : frame to send, interface handle, gateway address & flags
*
* Returns : nothing
*
* Operation : The frame is sent to the netrom interface, assuming that
* the arp table contains an entry for the gateway that will
* tell us what node callsign to use !
* The frame is sent by appending it to the L3 send queue
* -------------------------------------------------------------------------*/
unsigned nr_iface( mhbp, iface, gateway, tos )
mhtyp *mhbp;
unsigned iface;
ipaddr *gateway;
unsigned tos;
{
register unsigned i;
register mhtyp *mb;
register ARP_TAB *arp;
/* check if there is a netrom entry in arp table. If so, then
* check if there is a corresponding netrom node entry and set
* despoi to point to its identity
*/
if( (arp = res_arp( gateway, ARP_NETROM ) ) != NULLARP &&
iscall( arp->callsign ) )
{
l4pidx = l4pcid = NR_PROTO_IP; /* netrom IP family */
l4ahd2 = l4ahd3 = 0; /* these are unused for IP */
l4aopc = NR4_OP_PID; /* netrom proto extension */
mb = gennhd(); /* create a L4 & L3 header */
rwndmb( mhbp ); /* rewind to start for send */
#ifdef MODIFIED
mhtyp_copy( mhbp, mb );
#else
i = mhbp->putcnt - mhbp->getcnt;/* then copy frame onto hdr */
while( i-- )
putchr( getchr( mhbp ), mb );
#endif
mb->l2lnk = despoi; /* link points to dest node */
relink( mb, l3txl.lprev ); /* and queue it ! */
}
dealmb( mhbp );
}
/* ***************************************************************************
* Function : AX.25 handler for ip router
*
* Inputs : as for above interface to netrom
*
* Returns : nothing
*
* Operation : determine if dg or vc. Queue for sending as appropriate
* -------------------------------------------------------------------------*/
unsigned l2_iface( mhbp, iface, gateway, tos )
mhtyp *mhbp;
unsigned iface;
ipaddr *gateway;
unsigned tos;
{
register ARP_TAB *arp;
register unsigned cnt;
unsigned port;
register l2ltyp *l2poi;
/* determine tnc port number, rewind buffer & set PID to IP
*/
port = interfaces[iface].port;
rwndmb( mhbp );
mhbp->pid = PID_IP;
/* find the arp entry. Die if none !
*/
if( (arp = res_arp( gateway, ARP_AX25 ) ) == NULLARP )
{
arp_request( gateway, ARP_AX25, port );
dealmb( mhbp );
}
/* Now check to see if we are going to DG or VC it
*/
else if( (arp->dgmode & 1) || (( arp->dgmode == 0 ) && ( tos & DELAY ||
( !( tos & RELIABILITY ) && ( ipL2Modes & (1<<port))))))
{
sdui( nodigi, arp->callsign, myid, port, mhbp );
dealmb( mhbp );
}
else /* virtual circuit */
{
/* Need to find current L2 connection or need to make one
*/
for( l2poi = 0, cnt = 0, lnkpoi = &lnktbl[0];
cnt < MAXL2L; ++cnt, ++lnkpoi )
{
if( lnkpoi->state != 0 )
{
if( cmpid( arp->callsign, lnkpoi->dstid ) &&
cmpid( myid, lnkpoi->srcid ) &&
lnkpoi->liport == port )
break;
}
else if( (l2poi == 0 ) && ( lnkpoi->srcid[0] == 0 ) )
l2poi = lnkpoi;
}
/* either we have it or we are at the end, with no space
* or a pointer to the first free entry in the link list
*/
if( cnt == MAXL2L )
{
if( l2poi != 0 )
{
lnkpoi = l2poi;
cpyid( lnkpoi->srcid, myid );
cpyid( lnkpoi->dstid, arp->callsign );
cpyidl(lnkpoi->viaid, nodigi );
lnkpoi->liport = port;
newlnk();
}
else /* no link and no space for one */
{
dealmb( mhbp );
return;
}
}
lnkpoi->tosend++;
relink( mhbp, lnkpoi->sendil.lprev );
}
}
/* ***************************************************************************
* Function : find an arp entry for a given target and hardware type
*
* Inputs : pointer to internet address and a hardware ( arp ) type
*
* Returns : pointer to entry in arp table or a null pointer
*
* Operation : linear search of table looking for a match
* -------------------------------------------------------------------------*/
ARP_TAB *res_arp( target, hwtype )
register unsigned hwtype;
register ipaddr *target;
{
register ARP_TAB_MB *arprp;
for( arprp = (ARP_TAB_MB *)Arp_tab.lnext;
arprp != (ARP_TAB_MB *)&Arp_tab.lnext;
arprp = (ARP_TAB_MB *)arprp->link.lnext )
if( hwtype == arprp->arp.hwtype &&
#ifdef USELONG
arprp->arp.dest.Long == target->Long )
#else
arprp->arp.dest.Short[0] == target->Short[0] &&
arprp->arp.dest.Short[1] == target->Short[1] )
#endif
return( &arprp->arp );
return( NULLARP );
}
/* ***************************************************************************
* Function : Get a sixteen bit word from buffer
*
* Inputs : pointer to buffer
*
* Returns : unsigned integer
*
* Operation : reads two bytes into a structure & swaps in process.
* -------------------------------------------------------------------------*/
unsigned get16( mbhd )
register mhtyp *mbhd;
{
union {
unsigned Short;
unsigned char Bytes[2];
} regs;
regs.Bytes[1] = getchr( mbhd );
regs.Bytes[0] = getchr( mbhd );
return( regs.Short );
}
/* ***************************************************************************
* Function : Put an unsigned integer as two bytes in buffer
*
* Inputs : value to put and pointer to buffer
*
* Returns : nothing
*
* Operation :
* -------------------------------------------------------------------------*/
VOID put16( value, mbhd )
register unsigned value;
register mhtyp *mbhd;
{
union {
unsigned Short;
unsigned char Bytes[2];
} regs;
regs.Short = value;
putchr( regs.Bytes[1], mbhd );
putchr( regs.Bytes[0], mbhd );
}
/* ***************************************************************************
* Function : Return an ICMP response to the sender of a datagram.
* Unlike most routines, the callER frees the mbuf.
*
* Inputs : pointer to offending ip header, data, icmp codes & params
*
* Returns : nothing in this version !
*
* Operation : Did you ever hear the one about the vampire rabbit ?
* Note lots of standard icmp bits are commented out.
* -------------------------------------------------------------------------*/
int icmp_output(ip,data,type,code,args)
IP *ip; /* Header of offending datagram */
register mhtyp *data; /* Data portion of datagram */
unsigned type,code; /* Codes to send */
union icmp_args *args;
{
mhtyp *bp, *bp2;
ICMP icmp; /* ICMP protocol header */
unsigned short length; /* Total length of reply */
register unsigned short i;
char *rxnxt;
if(ip == NULLIP)
return(-1);
if( (unsigned char)ip->protocol == ICMP_PTCL)
{
/* Peek at type field of ICMP header to see if it's safe to
* return an ICMP message
*/
rxnxt = data->nxtchr;
i = getchr( data ) & 0xff;
data->nxtchr = rxnxt;
data->getcnt--;
switch( i )
{
case ICMP_ECHO_REPLY:
case ICMP_ECHO:
case ICMP_TIMESTAMP:
case ICMP_TIME_REPLY:
case ICMP_INFO_RQST:
case ICMP_INFO_REPLY:
break; /* These are all safe */
default:
/* Never send an ICMP error message about another
* ICMP error message!
*/
return(-1);
}
}
/* Compute amount of original datagram to return.
* We return the original IP header, and up to 8 bytes past that.
*/
i = data->putcnt - data->getcnt;
if( i > 8 ) i = 8;
length = i + ICMPLEN + IPLEN + ip->optlen;
/* Recreate and tack on offending IP header */
bp = htonip(ip, NULLBUF, 1);
/* if( data == NULLBUF)
{
icmpOutErrors++;
return(-1);
}
*/
/* Take excerpt from data portion */
while( i-- ) /* i set in above !!! */
putchr( getchr( data ), bp );
icmp.type = type;
icmp.code = code;
icmp.args.unused = 0;
switch( type )
{
/* case ICMP_ECHO:
icmpOutEchos++;
break;
case ICMP_ECHO_REPLY:
icmpOutEchoReps++;
break;
case ICMP_INFO_RQST:
break;
case ICMP_INFO_REPLY:
break;
case ICMP_TIMESTAMP:
icmpOutTimestamps++;
break;
case ICMP_ADDR_MASK:
icmpOutAddrMasks++;
break;
case ICMP_ADDR_MASK_REPLY:
icmpOutAddrMaskReps++;
break;
case ICMP_TIME_EXCEED:
icmpOutTimeExcds++;
break;
case ICMP_QUENCH:
icmpOutSrcQuenchs++;
break;
*/ case ICMP_PARAM_PROB:
/* icmpOutParmProbs++;
*/ icmp.args.pointer = args->pointer;
break;
case ICMP_REDIRECT:
/* icmpOutRedirects++;
*/ icmp.args.address = args->address;
break;
case ICMP_TIME_REPLY:
/* icmpOutTimestampReps++;
*/ icmp.args.echo.id = args->echo.id;
icmp.args.echo.seq = args->echo.seq;
break;
case ICMP_DEST_UNREACH:
if(icmp.code == ICMP_FRAG_NEEDED)
icmp.args.mtu = args->mtu;
/* icmpOutDestUnreachs++;
*/ break;
}
/* Now stick on the ICMP header */
bp2 = htonicmp(&icmp,bp);
dealmb( bp );
/* if(bp2 == NULLBUF)
return(-1);
*/ return( ip_send(&my_ip_addr,&ip->source,ICMP_PTCL,ip->tos,0,bp2,length,0,0));
}
/* ***************************************************************************
* Function : Generate ICMP header in network byte order, link data,
* compute checksum
*
* Inputs : pointer to icmp header structure in host format
* and pointer to data to be appended
*
* Returns : Pointer to new frame in network format
*
* Operation : Well, there was this traveller in Transylvania,
* -------------------------------------------------------------------------*/
mhtyp *htonicmp(icmp,data)
register ICMP *icmp;
mhtyp *data;
{
register mhtyp *bp;
unsigned short checksum;
register unsigned putcnt;
char *nxtchr, *cksum_ptr;
bp = (mhtyp *)allocb();
putchr( icmp->type, bp );
putchr( icmp->code, bp );
cksum_ptr = bp->nxtchr;
put16( 0, bp ); /* Clear checksum */
switch(icmp->type)
{
case ICMP_DEST_UNREACH:
put16( 0, bp );
if(icmp->code == ICMP_FRAG_NEEDED)
/* Deering/Mogul max MTU indication */
put16( icmp->args.mtu, bp);
else
put16( 0, bp);
break;
case ICMP_PARAM_PROB:
putchr( icmp->args.pointer, bp );
putchr( 0, bp );
put16( 0, bp );
break;
case ICMP_REDIRECT:
put16( icmp->args.address.Short[1], bp );
put16( icmp->args.address.Short[0], bp );
break;
case ICMP_ECHO:
case ICMP_ECHO_REPLY:
case ICMP_TIMESTAMP:
case ICMP_TIME_REPLY:
case ICMP_INFO_RQST:
case ICMP_INFO_REPLY:
put16( icmp->args.echo.id, bp );
put16( icmp->args.echo.seq, bp );
break;
default:
put16( 0, bp );
put16( 0, bp );
break;
}
rwndmb( data );
putcnt = data->putcnt;
while( putcnt-- )
putchr( getchr( data ) , bp );
/* Compute checksum, and stash result */
putcnt = bp->putcnt;
rwndmb( bp );
checksum = cksum(NULLHEADER,bp, bp->putcnt);
bp->nxtchr = cksum_ptr;
bp->putcnt = 2;
put16( checksum, bp );
bp->putcnt = putcnt;
rwndmb( bp );
return(bp);
}
/* ***************************************************************************
* Function : Reduced icmp input routine for echo only
*
* Inputs : ip header decoded plus data in the ip frame
*
* Returns : nothing
*
* Operation : Perform a simple icmp input and ip receive function
* to allow ping ( icmp echo request / reply )
* If valid, uses ipsend to add to the send queue
* -------------------------------------------------------------------------*/
VOID icmp_input( ip, bp )
register IP *ip;
register mhtyp *bp;
{
mhtyp *tbp;
register mhtyp *bp2;
ICMP icmp;
unsigned length, i;
length = ip->length - IPLEN - ip->optlen;
if( cksum( NULLHEADER, bp, length ) != 0 )
return;
if( bp->putcnt - bp->getcnt < 8 )
return;
icmp.type = getchr( bp );
icmp.code = getchr( bp );
if( icmp.type == ICMP_ECHO )
{
get16( bp );
icmp.args.echo.id = get16( bp );
icmp.args.echo.seq = get16( bp );
icmp.type = ICMP_ECHO_REPLY;
bp2 = allocb();
if ( length > 8 )
{
i = length - 8;
while( i-- )
putchr( getchr( bp ), bp2 );
}
tbp = htonicmp( &icmp, bp2 );
dealmb( bp2 );
ip_send( &ip->dest, &ip->source, ICMP_PTCL,
ip->tos, 0, tbp, length, 0, 0 );
}
}
/* ***************************************************************************
* Function : Perform end-around-carry adjustment
*
* Inputs : current checksum if using longs
* ( if not, it uses a static structure ! )
*
* Returns : corrected unsigned integer checksum with carries
*
* Operation : Add the accumulated carry bits into the lower 16 bits
* and keep doing it until you have no more carries
* -------------------------------------------------------------------------*/
#ifdef USELONG
unsigned short eac(sum)
long sum; /* Carries in high order 16 bits */
{
register unsigned short csum;
while((csum = sum >> 16) != 0)
sum = csum + (sum & 0xffffL);
return((unsigned short) (sum & 0xffffl)); /* Chops to 16 bits */
}
#else
unsigned short eac()
{
register unsigned short csum;
while( ( csum = ip_cksum.Short[1] ) != 0 )
{
ip_cksum.Short[1] = 0;
addsum( csum );
}
return( ip_cksum.Short[0] );
}
#endif
/* ***************************************************************************
* Function : Checksum an mhtyp, with optional pseudo-header
*
* Inputs : pseudo header, buffer to checksum and byte count
*
* Returns : unsigned integer checksum with eac corrected
*
* Operation : add up all bytes in a long, in perverse arpa way then eac
* -------------------------------------------------------------------------*/
unsigned short cksum(ph,m,len)
register unsigned short len;
register struct pseudo_header *ph;
register mhtyp *m;
{
long sum;
unsigned getcnt;
char *nxtchr;
#ifdef USELONG
sum = 0L;
#else
ip_cksum.Short[0] = ip_cksum.Short[1] = 0;
#endif
/* Sum pseudo-header, if present */
if(ph != NULLHEADER)
{
#ifdef USELONG
sum = (unsigned)ph->source.Short[0];
sum += (unsigned)ph->source.Short[1];
sum += (unsigned)ph->dest.Short[0];
sum += (unsigned)ph->dest.Short[1];
sum += (unsigned)ph->protocol & 0xff;
sum += (unsigned)ph->length;
#else
addsum( ph->source.Short[0] );
addsum( ph->source.Short[1] );
addsum( ph->dest.Short[0] );
addsum( ph->dest.Short[1] );
addsum( ph->protocol & 0xff );
addsum( ph->length );
#endif
}
getcnt = m->getcnt;
nxtchr = m->nxtchr;
while( len > 0 )
{
if( len > 1 )
{
#ifdef USELONG
sum += (unsigned)get16( m );
#else
addsum( get16( m ) );
#endif
len -= 2;
}
else
{
#ifdef USELONG
sum += ( (unsigned)getchr( m ) << 8 ) & 0xff00 ;
#else
addsum( ( getchr( m ) << 8 ) & 0xff00 );
#endif
len--;
}
}
m->getcnt = getcnt;
m->nxtchr = nxtchr;
/* Do final end-around carry, complement and return */
#ifdef USELONG
return( (unsigned short)(~eac(sum) & 0xffff));
#else
return( ~eac() );
#endif
}
/* ***************************************************************************
* Function : for those systems that avoid longs, mask an ip address
* off to (bits) bits
*
* Inputs : pointer to address and number of bits to leave
*
* Returns : nothing
*
* Operation : perform ip style 32 bit masking as two shorts.
* -------------------------------------------------------------------------*/
#ifndef USELONG
VOID ip_mask( targt, bits )
unsigned bits;
ipaddr *targt;
{
register unsigned i = 32 - bits;
register ipaddr *target = targt;
if( i == 0 )
return;
else if( i < 16 )
target->Short[0] &= ( ~0 << ( i ) );
else
{
target->Short[0] = 0;
if( i > 16 )
target->Short[1] &= ( ~0 << ( i - 16 ) );
}
}
#endif
/* ***************************************************************************
* Function : for those systems that avoid longs, add to checksum
*
* Inputs : 16 bit unsigned to add to 32 bit checksum
*
* Returns : nothing
*
* Operation : add the value to the static checksum ( unsigned long )
* Note : for proper processors, #define does it with longs !
* -------------------------------------------------------------------------*/
#ifndef addsum
#asm
.z80
public addsum_
addsum_:
pop hl
pop de
push de
push hl
ld hl,(ip_cksum_)
add hl,de
ld (ip_cksum_),hl
ret nc
ld hl,(ip_cksum_ + 2)
inc hl
ld (ip_cksum_ + 2 ),hl
ret
.8080
#endasm
#endif
/* the following routines are needed for the arp protocol handler
*/
/* ***************************************************************************
* Function : Main handler for ARP requests from the network
*
* Inputs : pointer to the input frame from the L2 handler
*
* Returns : Nothing.
*
* Outputs : UI frame with the result of the processing ( if needed )
*
* Operation : Unpack the frame. Check it is OK, and is either for us
* or for a published entry or a revarp request we can deal
* with. If it is, form the reply and send a UI frame.
* -------------------------------------------------------------------------*/
VOID arp_service( mhbp )
mhtyp *mhbp;
{
unsigned char arp_not_revarp;
char *ptr;
register mhtyp *mbhd = mhbp;
register ARP_TAB *ap;
register unsigned i;
ARP_TAB_MB *atp;
if( !ARPrunning || (unsigned char)mbhd->pid != PID_ARP )
return;
if(( arp.hardware = get16( mbhd )) != ARP_AX25 )
{
/* arp_stat.badtype++; */
return;
}
if( (arp.protocol = get16( mbhd ) ) != PID_IP )
{
/* arp_stat.badtype++; */
return;
}
arp.hwalen = getchr( mbhd );
arp.pralen = getchr( mbhd );
if( arp.hwalen > MAXHWALEN || arp.pralen != sizeof( ipaddr ) )
{
/* arp_stat.badlen++; */
return;
}
arp.opcode = get16( mbhd );
getfid( arp.shwaddr, mbhd );
arp.sprotaddr.Short[1] = get16( mbhd );
arp.sprotaddr.Short[0] = get16( mbhd );
getfid( arp.thwaddr, mbhd );
arp.tprotaddr.Short[1] = get16( mbhd );
arp.tprotaddr.Short[0] = get16( mbhd );
if( cmpid( arp.shwaddr, QST ) )
{
/* arp_stat.badaddr++; */
return;
}
ap = res_arp( &arp.sprotaddr, arp.hardware );
if( ((i=is_my_ip_addr( &arp.tprotaddr)) && ap == NULLARP )
||
( ap != NULLARP && ap->timer != 0 ) )
arp_add( &arp.sprotaddr, arp.hardware, arp.shwaddr, 0, ARPtimer, 0 );
if( arp.opcode == REVARP_REQUEST
||
( arp.opcode == ARP_REQUEST
&&
( ( i /* = is_my_ip_addr( &arp.tprotaddr ) */ )
||
( ( ap = res_arp( &arp.tprotaddr, arp.hardware )) != NULLARP
&& ap->publish_flag
)
)
)
)
{
arp_not_revarp = ( arp.opcode == ARP_REQUEST );
if( !arp_not_revarp )
for( atp = ( ARP_TAB_MB * )Arp_tab.lnext;
atp != ( ARP_TAB_MB * )&Arp_tab.lnext;
atp = ( ARP_TAB_MB * )atp->link.lnext )
if( ( i = cmpid( (ap=&(atp->arp))->callsign, arp.thwaddr ) ) )
break;
if( arp_not_revarp || ( i && ap->publish_flag ) )
{
if( arp_not_revarp )
{
cpyid( arp.thwaddr, arp.shwaddr );
/* if( arp.hardware == ARP_AX25 ) */
arp.thwaddr[arp.hwalen - 1 ] |= 1;
}
cpyid( arp.shwaddr, i ? myid : ap->callsign );
#ifdef USELONG
arp.tprotaddr.Long = arp_not_revarp ?
arp.sprotaddr.Long : ap->dest.Long;
arp.sprotaddr.Long = i ?
my_ip_addr.Long : ap->dest.Long;
#else
arp.tprotaddr.Short[1] = arp_not_revarp ?
arp.sprotaddr.Short[1] : ap->dest.Short[1];
arp.tprotaddr.Short[0] = arp_not_revarp ?
arp.sprotaddr.Short[0] : ap->dest.Short[0];
arp.sprotaddr.Short[1] = i ?
my_ip_addr.Short[1] : ap->dest.Short[1];
arp.sprotaddr.Short[0] = i ?
my_ip_addr.Short[0] : ap->dest.Short[0];
#endif
arp.opcode = arp_not_revarp ? ARP_REPLY : REVARP_REPLY;
arp_send( mhbp->l2port, arp.thwaddr );
/* Arp_stat.inreq++; */
}
}
}
/* ***************************************************************************
* Function : Send an ARP request/response frame to specified port ( L2 AX25 )
*
* Inputs : pointer to the host format arp frame and L2 port number
*
* Returns : Nothing.
*
* Outputs : UI frame with the ARP request
*
* -------------------------------------------------------------------------*/
arp_send( port, hwaddr )
unsigned port;
char *hwaddr;
{
register mhtyp *mbhd;
mbhd = allocb();
put16( arp.hardware, mbhd );
put16( arp.protocol, mbhd );
putchr( arp.hwalen, mbhd );
putchr( arp.pralen, mbhd );
put16( arp.opcode, mbhd );
putfid( arp.shwaddr, mbhd );
put16( arp.sprotaddr.Short[1], mbhd );
put16( arp.sprotaddr.Short[0], mbhd );
putfid( arp.thwaddr, mbhd );
put16( arp.tprotaddr.Short[1], mbhd );
put16( arp.tprotaddr.Short[0], mbhd );
/* if( mbhd == NULLBUF ) return; */
mbhd->pid = PID_ARP;
rwndmb( mbhd );
sdui( nodigi, hwaddr, myid, port, mbhd );
dealmb( mbhd );
}
arp_request( gw, hwtype, port )
ipaddr *gw;
unsigned hwtype;
unsigned port;
{
register unsigned i;
register ipaddr *gateway = gw;
arp.hardware = hwtype;
arp.protocol = PID_IP;
arp.hwalen = L2IDLEN;
arp.pralen = sizeof( ipaddr );
arp.opcode = ARP_REQUEST;
cpyid( arp.shwaddr, myid );
#ifdef USELONG
arp.sprotaddr.Long = my_ip_addr.Long;
arp.tprotaddr.Long = gateway->Long;
#else
arp.sprotaddr.Short[0] = my_ip_addr.Short[0];
arp.sprotaddr.Short[1] = my_ip_addr.Short[1];
arp.tprotaddr.Short[0] = gateway->Short[0];
arp.tprotaddr.Short[1] = gateway->Short[1];
#endif
for( i=0; i<L2IDLEN; i++ )
arp.thwaddr[i] = 0;
arp_send( port, QST );
}